﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;

using System.Numerics;

namespace Dwarf.Managed
{
    /// <summary>
    /// Settings taken from command line and input file.
    /// </summary>
    class Settings
    {

        /// <summary>
        /// Input & output files.
        /// </summary>
        private String inputFile;
        private String profileFile;
        private String resultFile;


        /// <summary>
        /// Short dwarf's description.
        /// </summary>
        private const String DWARF_NAME = "Spectral method, managed serial kernel.";

        /// <summary>
        /// Constants for output files's names
        /// </summary>
        private const String DWARF_NAME_OUTPUT = "Dwarf.Managed.Serial";
        private const String PROFILE_TXT = ".Profile.txt";
        private const String RESULT_TXT = ".Result.txt";

        /// <summary>
        /// Default size for buffer.
        /// </summary>
        private const int BUFFER_SIZE = 1024;

        public Settings()
        {
            inputFile = new String(new char[BUFFER_SIZE]);
            profileFile = new String(new char[BUFFER_SIZE]);
            resultFile = new String(new char[BUFFER_SIZE]);
        }

        /// <summary>
        /// Start the time count.
        /// </summary>
        public void Start()
        {
            start();
        }

        /// <summary>
        /// Problem results output.
        /// </summary>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        public void Finish(Solver solver)
        {
            double time = 0; ;
            stop(ref time);

            Console.WriteLine();
            Console.WriteLine("Clock time (sec): {0}", time.ToString("F8"));

            this.WriteResults(solver, time);
        }

        /// <summary>
        /// Init variables, parse cli params and input file.
        /// </summary>
        /// <param name="args">
        /// Cli params.
        /// </param>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        public void Init(String[] args, Solver solver)
        {
            //Parse cli params.
            parseArguments(
                    args.Length,
                    args,
                    ref inputFile,
                    ref profileFile,
                    ref resultFile);

            //Parse input file.
            this.parseConfigFile(solver);

            // write settings if it's needed
            this.WriteSettings(solver);
        }

        private void WriteResults(Solver solver, double time)
        {
                try
                {
                    using (StreamWriter outputResult = File.CreateText(resultFile))
                    {
                        using (StreamWriter outputProfile = File.CreateText(profileFile))
                        {
                            outputProfile.WriteLine("#Dwarf name:{0}", DWARF_NAME);

                            outputProfile.WriteLine("#Time: {0}", DateTime.Now.ToString());

                            outputProfile.WriteLine("#Length: {0}", solver.length);
                            outputProfile.WriteLine("#Result time (sec): {0}", time.ToString("F8"));
                            outputResult.WriteLine("#Result domain:");
                            for (int i = 0; i < solver.length; i++)
                            {
                                for (int j = 0; j < solver.length; j++)
                                {
                                    Complex c = solver.complexArray[i][j];
                                    outputResult.Write(" {0:F1} {1:F1} ;", c.Real, c.Imaginary);
                                }
                                outputResult.WriteLine();
                            }
                            outputResult.WriteLine();
                            outputProfile.WriteLine("#eof");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw new Exception("Error in output file");
                }
            
        }

        /// <summary>
        /// Parse input file into solver's structures.
        /// </summary>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        private void parseConfigFile(Solver solver)
        {
            // Open the file to read from.
            try
            {
                using (StreamReader sr = File.OpenText(inputFile))
                {
                    solver.length = int.Parse(GetLine(sr));
                    solver.complexArray = new Complex[solver.length][];
                    for (int i = 0; i < solver.length; i++)
                    {
                        solver.complexArray[i] = new Complex[solver.length];
                    }

                    String[] str;
                    for (int j = 0; j < solver.length; j++)
                    {
                        str = GetLine(sr).Split(';');
                        String[] complexStr;
                        for (int i = 0; i < solver.length; i++)
                        {
                            complexStr = str[i].Split(' ');
                            solver.complexArray[j][i] = new Complex(double.Parse(complexStr[0]), double.Parse(complexStr[1]));
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw new Exception("Error in input file");
            }
        }

        /// <summary>
        /// Write settings.
        /// </summary>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        private void WriteSettings(Solver solver)
        {
            // Write general settings.
            Console.WriteLine("Kernel settings summary : ");
            Console.WriteLine("Dwarf name              : {0} ", DWARF_NAME);
            Console.WriteLine("Length                  : {0} ", solver.length);
            Console.WriteLine("Input file               : {0} ", inputFile);
            Console.WriteLine("Output file              : {0} ", resultFile);
            Console.WriteLine();
        }

        /// <summary>
        /// Get one line from stream.
        /// </summary>
        /// <param name="solver">
        /// Stream to get from.
        /// </param>
        private static String GetLine(StreamReader sr)
        {
            String s = sr.ReadLine();
            //Avoid comments lines.
            while (s.StartsWith("#") || s.StartsWith("//"))
            {
                s = sr.ReadLine();
            }

            return s;

        }

        /// <summary>Parse the command line arguments and fill the reference parameters</summary>
        /// <param name="argc">Count of command line tokens</param>
        /// <param name="argv">Command line tokens</param>
        /// <param name="log">Settings log (debug/benchmark)</param>
        /// <param name="inputFile">Name of input text file</param>
        /// <param name="outputFile">Name of output file</param>
        [DllImport(@"CliTools.dll")]
        private static extern void parseArguments(
            int argc,
            String[] argv,
            ref string inputFile,
            ref string profileFile,
            ref string resultFile
            );

        [DllImport(@"CliTools.dll")]
        private static extern void start();

        [DllImport(@"CliTools.dll")]
        private static extern void stop(ref double time);
    }
}